# -*- coding: utf-8 -*-

from copy import deepcopy
import logging

from odoo import models, fields, api
from odoo.exceptions import UserError, ValidationError

from . cfprint_templates import *

_logger = logging.getLogger(__name__)


class BuliaoIncomingOrder(models.Model):
    _name = 'buliao.incoming.order'
    _inherit = ['mail.thread']

    name = fields.Char(u'入库单号', default=lambda self: u'新建', readonly=True, index=True, copy=False)
    plan_name = fields.Char(u'生产计划号', default=lambda self: u'新建', readonly=True, index=True, copy=False)
    outgoing_name = fields.Char(u'出库单号', default=lambda self: u'新建', readonly=True, index=True, copy=False)
    order_date = fields.Datetime(u'创建时间', default=fields.Datetime.now, index=True, copy=False, readonly=True,
                                 states={'draft': [('readonly', False)]}, help=u'入库单创建时间')
    confirm_date = fields.Datetime(u'入库时间', index=True, readonly=True)
    plan_date = fields.Datetime(u'排产时间', index=True, readonly=True)
    running_date = fields.Datetime(u'开始时间', index=True, readonly=True, store=True, compute='_compute_state')
    done_date = fields.Datetime(
        u'完成时间', index=True, readonly=True, help=u'生产完成的时间', store=True, compute='_compute_state')
    outgoing_date = fields.Datetime(u'出库时间', index=True, readonly=True)
    pricelist_id = fields.Many2one('product.pricelist', string=u'价格表', required=True, copy=True)
    # 货币需要使用存储字段，用到Monetary字段的地方需要货币来进行转换。比如报告中
    currency_id = fields.Many2one(
        'res.currency', string=u'货币', related='pricelist_id.currency_id', required=True, store=True, copy=True)
    amount_total = fields.Monetary(u'总价', readonly=True, store=True, compute='_compute_amount_total')
    compute_method = fields.Selection(
        [('weight', u'毛重'), ('net_weight', u'净重')], string=u'结算方式', default='weight', required=True,
        states={'pay': [('readonly', True)]}
    )
    state = fields.Selection([
        ('draft', u'草稿'),
        ('confirm', u'已入库'),
        ('plan', u'生产计划'),
        ('running', u'正在生产'),
        ('produce_done', u'生产完成'),
        ('outgoing', u'已出库'),
        ('pay', u'已付款')
    ], string=u'单据状态', default='draft', copy=False, index=True, readonly=True, store=True, compute='_compute_state',
        track_visibility='onchange')
    partner_id = fields.Many2one(
        'res.partner', u'客户', readonly=True, required=True, states={'draft': [('readonly', False)]})
    user_id = fields.Many2one(
        'res.users', string=u'制单员', index=True, track_visibility='onchange', default=lambda self: self.env.user)
    so_ids = fields.One2many('sale.order', 'incoming_order_id', u'销售订单')
    running_so_id = fields.Many2one(u'sale.order', u'正在生产', readonly=True, compute='_compute_state')
    note = fields.Char(u'备注')

    @api.constrains('partner_id')
    def _check_partner_id(self):
        if self.so_ids and self.so_ids.filtered(lambda r: r.partner_id != self.partner_id):
            raise ValidationError(u'入库单客户与销售订单客户不一致！')

    @api.one
    @api.depends('so_ids.amount_total')
    def _compute_amount_total(self):
        self.amount_total = sum(self.so_ids.mapped('amount_total'))

    @api.one
    @api.depends('so_ids.produce_state')
    def _compute_state(self):
        if self.so_ids:
            if any(so.produce_state == 'running' for so in self.so_ids):
                self.state = 'running'
                self.running_date = fields.Datetime.now()
            elif all(so.produce_state == 'done' for so in self.so_ids):
                self.state = 'produce_done'
                self.done_date = fields.Datetime.now()
            elif any(so.produce_state == 'done' for so in self.so_ids):
                self.state = 'plan'
        if self.state == 'running':
            for so in self.so_ids:
                if so.produce_state == 'running':
                    self.running_so_id = so.id
        else:
            self.running_so_id = False
        if not self.state:
            self.state = 'draft'

    @api.multi
    def write(self, vals):
        res = super(BuliaoIncomingOrder, self).write(vals)
        if 'compute_method' in vals:
            for in_order in self:
                if in_order.state in ['produce_done', 'outgoing']:
                    if vals['compute_method'] == 'weight':
                        for so in self.so_ids:
                            so.order_line.update({'product_uom_qty': so.buliao_weight})
                    elif vals['compute_method'] == 'net_weight':
                        for so in self.so_ids:
                            so.order_line.update({'product_uom_qty': so.buliao_net_weight})
        return res

    # 在onchange中改变其他模型的字段值无效。onchange针对当前表单。最简单的方法是为line的数量添加一个计算方法，但是会修改odoo源码的字段
    # @api.onchange('compute_method')
    # def onchange_compute_method(self):
    #     if self.state in ['produce_done', 'outgoing']:
    #         if self.compute_method == 'weight':
    #             for so in self.so_ids:
    #                 so.order_line.update({'product_uom_qty': so.buliao_weight})
    #         elif self.compute_method == 'net_weight':
    #             for so in self.so_ids:
    #                 so.order_line.update({'product_uom_qty': so.buliao_net_weight})

    # 在so列表中声明一个隐藏的partner_id字段，当入库单的客户变更时，触发该方法可以改变so的客户，但是不会触发so中partner_id定义的onchange
    @api.onchange('partner_id')
    def onchange_partner_id(self):
        res = {}
        warning = {}
        # 在表单中需要存储价格表字段(可以隐藏)。如果不存在，不会像服务端提交
        self.pricelist_id = \
            self.partner_id.property_product_pricelist and self.partner_id.property_product_pricelist.id or False,
        if any(r.partner_id != self.partner_id for r in self.so_ids):
            warning = {
                'title': u'注意',
                'message': u'入库单的客户改变后需要手动修改销售订单的客户！'
            }
        if warning:
            res['warning'] = warning
        return res

        # self.so_ids.update({'partner_id': self.partner_id})

    # @api.multi
    # def write(self, vals):
    #     # 这种方式不会触发sale.order模型为partner_id字段定义的onchange方法
    #     if 'partner_id' in vals:
    #         changes = self.so_ids.filtered(lambda r: r.partner_id != self.partner_id)
    #         # 直接使用write时需要使用数字id
    #         changes.update({'partner_id': vals.get('partner_id', self.partner_id)})
    #     return super(BuliaoIncomingOrder, self).write(vals)

    @api.model
    def create(self, vals):
        field_codes = {
            'name': 'buliao.incoming.order',
            'plan_name': 'buliao.produce.plan',
            'outgoing_name': 'buliao.outgoing.order'
        }
        for name, code in field_codes.items():
            if vals.get(name, u'新建') == u'新建':
                if 'company_id' in vals:
                    vals[name] = self.env['ir.sequence'].with_context(
                        force_company=vals['company_id']).next_by_code(code) or u'新建'
                else:
                    vals[name] = self.env['ir.sequence'].next_by_code(code) or u'新建'

        result = super(BuliaoIncomingOrder, self).create(vals)
        return result

    @api.multi
    def action_confirm(self):
        if self.so_ids:
            for so in self.so_ids:
                if not so.order_line:
                    raise UserError(u'销售订单:[%s] 没有指定工艺要求。' % so.name)
        else:
            raise UserError(u'入库单没有指定销售订单，不能入库。')
        self.so_ids.action_confirm()
        confirm_date = fields.Datetime.now()
        self.write({'state': 'confirm', 'confirm_date': confirm_date})
        return True

    @api.multi
    def action_plan(self):
        plan_date = fields.Datetime.now()
        # 入库单的状态依赖于so的produce_state，需要先改变so，然后再改变入库单
        self.so_ids.write({'produce_state': 'plan'})
        self.write({'state': 'plan', 'plan_date': plan_date})
        return True

    @api.multi
    def action_outgoing(self):
        outgoing_date = fields.Datetime.now()
        self.write({'state': 'outgoing', 'outgoing_date': outgoing_date})

    @api.multi
    def action_draft(self):
        warn_msgs = {
            'running': u'[%s]已经开始生产，计划单不能设置为草稿状态。',
            'produce_done': u'[%s]已经生产完毕，计划单不能设置为草稿状态。'
        }
        for so in self.so_ids:
            msg = warn_msgs.get(so.produce_state, '')
            if msg:
                raise UserError(msg % so.name)
            so.action_cancel()
            so.action_draft()
        self.so_ids.write({'produce_state': 'draft'})
        self.write({'state': 'draft', 'plan_date': False})
        return True

    def _prepare_incoming_order_print_data(self, report_data):
        table1_data = {
          "order_name": self.name,
          "partner": self.partner_id.name,
          "confirm_date": self.confirm_date,
          "user": self.user_id.name
        }
        table2_empty_data = {
          "buliao_type": "",
          "buliao_color": "",
          "buliao_qty": "",
          "buliao_weight": "",
          "line_names": ""
        }
        rows = self.env['ir.values'].get_default('buliao.config.settings', 'incoming_order_rows')
        table2_datas = []
        for so in self.so_ids:
            table2_data = so.mapped(lambda r: {
                'customer_origin': r.customer_origin or '',
                'buliao_type': r.buliao_type or '',
                'buliao_qty': str(r.buliao_qty),
                'buliao_weight': str(r.buliao_weight),
                'line_names': r.line_names or '',
                'buliao_note': r.buliao_note
            })[0]
            table2_datas.append(table2_data)
        # 如果数据少于定义的表格行数，则使用空值填充
        if len(self.so_ids) < rows:
            map(lambda r: table2_datas.append(table2_empty_data), xrange(rows-len(self.so_ids)))
        report_data['Tables'][0]['Data'] = [table1_data]
        report_data['Tables'][1]['Data'] = table2_datas
        report_data['Tables'][2]['Data'] = [{'user': self.env.user.name}]

    @api.multi
    def _prepare_plan_order_print_data(self, report_data):
        table1_data = {
            "plan_name": self.plan_name,
            "partner": self.partner_id.name,
            "plan_date": self.plan_date
        }
        table2_empty_data = {
            "so_name": "",
            "buliao_type": "",
            "buliao_color": "",
            "buliao_qty": "",
            "buliao_weight": "",
            "line_names": ""
        }
        rows = self.env['ir.values'].get_default('buliao.config.settings', 'plan_order_rows')
        table2_datas = []
        for so in self.so_ids:
            table2_data = so.mapped(lambda r: {
                'so_name': r.name,
                'buliao_type': r.buliao_type or '',
                'buliao_color': r.buliao_color or '',
                'buliao_qty': r.buliao_qty,
                'buliao_weight': r.buliao_weight,
                'line_names': r.line_names or '',
                'buliao_note': r.buliao_note
            })[0]
            table2_datas.append(table2_data)
        # 如果数据少于定义的表格行数，则使用空值填充
        if len(self.so_ids) < rows:
            map(lambda r: table2_datas.append(table2_empty_data), xrange(rows - len(self.so_ids)))
        report_data['Tables'][0]['Data'] = [table1_data]
        report_data['Tables'][1]['Data'] = table2_datas
        report_data['Tables'][2]['Data'] = [{'user': self.env.user.name, 'note': self.note or ''}]

    @api.multi
    def _prepare_outgoing_order_print_data(self, report_data):
        compute_method = ''
        if self.compute_method == 'weight':
            compute_method = u'按毛重'
        elif self.compute_method == 'net_weight':
            compute_method = u'按净重'

        table1_data = {
            "outgoing_name": self.outgoing_name,
            "partner": self.partner_id.name,
            "outgoing_date": self.outgoing_date,
            "compute_method": compute_method
        }
        table2_empty_data = {
          "customer_origin": "",
          "buliao_type": "",
          "buliao_qty": "",
          "buliao_net_weight": "",
          "buliao_weight": "",
          "buliao_unit_price": "",
          "amount_total": ""
        }
        rows = self.env['ir.values'].get_default('buliao.config.settings', 'outgoing_order_rows')
        table2_datas = []
        for so in self.so_ids:
            table2_data = so.mapped(lambda r: {
                'customer_origin': r.customer_origin or '',
                'buliao_type': r.buliao_type or '',
                'buliao_qty': r.buliao_qty,
                'buliao_net_weight': r.buliao_net_weight,
                'buliao_weight': r.buliao_weight,
                'buliao_unit_price': r.buliao_unit_price,
                'amount_total': r.amount_total
            })[0]
            table2_datas.append(table2_data)
        # 如果数据少于定义的表格行数，则使用空值填充
        if len(self.so_ids) < rows:
            map(lambda r: table2_datas.append(table2_empty_data), xrange(rows - len(self.so_ids)))
        report_data['Tables'][0]['Data'] = [table1_data]
        report_data['Tables'][1]['Data'] = table2_datas
        report_data['Tables'][2]['Data'] = [{
            'user': self.env.user.name,
            'amount_total': self.amount_total,
            'credit': str(self.partner_id.credit),
        }]

    @api.multi
    def action_cfprint(self):
        self.ensure_one()
        _reportData = {}
        printer_field_name = ''
        print_type = self.env.context.get('print_type', '')

        if print_type == 'incoming':
            # 单个打印之所以使用深度拷贝是为了防止多个用户同时修改全局字典，出现错误数据。
            _reportData = deepcopy(incoming_order_tmpl)
            printer_field_name = 'incoming_order_printer'
            self._prepare_incoming_order_print_data(_reportData)
        elif print_type == 'plan':
            _reportData = deepcopy(plan_order)
            printer_field_name = 'plan_order_printer'
            self._prepare_plan_order_print_data(_reportData)
        elif print_type == 'outgoing':
            _reportData = deepcopy(outgoing_order)
            printer_field_name = 'outgoing_order_printer'
            self._prepare_outgoing_order_print_data(_reportData)
        elif print_type == 'outgoing_detail':
            return self.so_ids.action_cfprint()

        printer_name = self.env['ir.values'].get_default('buliao.config.settings', printer_field_name)
        _reportData['Printer'] = printer_name
        return {
            'type': 'ir.actions.client',
            'tag': 'cfprint_report',
            'params': {
                '_reportData': _reportData
            }
        }

    @api.multi
    def action_print_plan(self):
        self.ensure_one()
        return {
            'type': 'ir.actions.client',
            'tag': 'open_report',
            'params': {
                'active_id': self.id,
                'report_name': 'lmch_buliao.report_produceplan',
                'report_type': 'html'
            }
        }

    @api.multi
    def action_print_outgoing(self):
        self.ensure_one()
        return {
            'type': 'ir.actions.client',
            'tag': 'open_report',
            'params': {
                'active_id': self.id,
                'report_name': 'lmch_buliao.report_outgoing',
                'report_type': 'html'
            }
        }

    @api.multi
    def action_pay(self):
        self.so_ids.action_invoice_create()
        self.state = 'pay'
        return self.so_ids.action_view_invoice()

    @api.multi
    def action_view_invoice(self):
        return self.so_ids.action_view_invoice()
